﻿namespace OA.Infrastructure.Impl;

internal class UserService : IUserService
{
    private readonly DbContext _context;

    public UserService(DbContext context)
    {
        _context = context;
    }

    public async Task<ListResult<User.QueryModel>> GetListAsync(CancellationToken cancellationToken)
    {
        var result = new ListResult<User.QueryModel>();

        try
        {
            var data = await _context.Set<User>()
                .Include(m => m.Roles)
                .Include(m => m.Department).ThenInclude(x => x.Leader)
                .Include(m => m.Department).ThenInclude(x => x.Parent)
                .Include(m => m.Department).ThenInclude(x => x.Roles)
                .AsNoTracking()
                .ToListAsync(cancellationToken);

            result.Data = data.Select(x => GetQueryModel(x));

            result.Status = OperationalResult.SUCCESS;
        }
        catch (Exception error)
        {
            result.Status = OperationalResult.ERROR;

            result.Message = error.ToString();
        }

        return result;
    }

    public async Task<PageResult<User.QueryModel>> GetPageAsync(User.ConditionSelectorCondition condition, CancellationToken cancellationToken)
    {
        var result = new PageResult<User.QueryModel>();

        try
        {
            var expression = GetConditionSelectorCondition(condition);

            var data = await _context.Set<User>()
                .Include(m => m.Roles)
                .Include(x => x.DirectLeader).ThenInclude(x => x.DirectLeader)
                .Include(x => x.DirectLeader).ThenInclude(x => x.Department)
                .Include(m => m.Department).ThenInclude(x => x.Leader)
                .Include(m => m.Department).ThenInclude(x => x.Parent)
                .Include(m => m.Department).ThenInclude(x => x.Roles)
                .Where(expression)
                .OrderBy(x => x.Id)
                .Skip((condition.PageNum - 1) * condition.PageSize)
                .Take(condition.PageSize)
                .AsNoTracking()
                .ToListAsync(cancellationToken);

            result.Data = data.Select(x => GetQueryModel(x));

            result.PageNum = condition.PageNum;

            result.PageSize = condition.PageSize;

            result.Total = await _context.Set<User>().CountAsync(expression, cancellationToken);

            result.Status = OperationalResult.SUCCESS;
        }
        catch (Exception error)
        {
            result.Status = OperationalResult.ERROR;

            result.Message = error.ToString();
        }

        return result;
    }

    public async Task<IEnumerable<string>> GetRoleCodesAsync(string account, CancellationToken cancellationToken)
    {
        var entity = await _context.Set<User>()
            .Include(n => n.Department)
            .Include(n => n.Roles)
            .Where(n => n.Account == account && !n.IsDeleted)
            .FirstOrDefaultAsync(cancellationToken);

        if (entity is null)
        {
            return Enumerable.Empty<string>();
        }

        IEnumerable<Role> roles;
        if (entity.Department is not null)
        {
            var dept = await _context.Set<Department>()
                .Include(n => n.Roles)
                .Where(n => n.Id == entity.Department.Id)
                .FirstOrDefaultAsync(cancellationToken);

            roles = dept!.Roles.Concat(entity.Roles);
        }
        else
        {
            roles = entity.Roles;
        }

        return roles.Select(n => n.RoleCode);
    }

    public async Task<string?> GetUserGrantsAsync(string account, CancellationToken cancellationToken)
    {
        var entity = await _context.Set<User>()
            .Include(n => n.Department).ThenInclude(n => n.Roles)
            .Include(n => n.Roles)
            .Select(n => new { n.Account, n.Roles, n.Department, n.IsDeleted })
            .Where(n => n.Account == account && !n.IsDeleted)
            .FirstOrDefaultAsync(cancellationToken);

        if (entity is null)
        {
            return null;
        }

        var types = entity.Roles.SelectMany(n => n.Types);

        if (entity.Department is not null)
        {
            var deptRoleTypes = entity.Department.Roles.SelectMany(n => n.Types);

            return string.Join(App.Separator, types.Union(deptRoleTypes));
        }

        return string.Join(App.Separator, types);
    }

    public async Task<IEnumerable<string>> GetRolesNameAsync(string account, CancellationToken cancellationToken)
    {
        var entity = await _context.Set<User>()
            .Include(n => n.Department).ThenInclude(n => n.Roles)
            .Include(n => n.Roles)
            .Select(n => new { n.Account, n.Roles, n.Department, n.IsDeleted })
            .Where(n => n.Account == account && !n.IsDeleted)
            .FirstOrDefaultAsync(cancellationToken);

        if (entity is null)
        {
            return Enumerable.Empty<string>();
        }

        if (entity.Department is not null)
        {
            var deptRoleTypes = entity.Department.Roles;

            return entity.Roles.Union(entity.Department.Roles).Select(x => x.Name);
        }

        return entity.Roles.Select(x => x.Name);
    }

    public async Task<OperationalResult<User.QueryModel>> GetUserInfoAsync(string account, CancellationToken cancellationToken)
    {
        var result = new OperationalResult<User.QueryModel>();

        var entity = await _context.Set<User>()
            .Where(n => n.Account == account && !n.IsDeleted)
            .FirstOrDefaultAsync(cancellationToken);

        if (entity is null)
        {
            result.Status = OperationalResult.FAILURE;

            return result;
        }

        result.Status = OperationalResult.SUCCESS;
        result.Data = GetQueryModel(entity);

        return result;
    }

    public async Task<IEnumerable<string>> GetUserRoutesAsync(string account, CancellationToken cancellationToken)
    {
        var entity = await _context.Set<User>()
            .Include(n => n.Department).ThenInclude(n => n.Roles)
            .Include(n => n.Roles)
            .Where(n => n.Account == account && !n.IsDeleted)
            .FirstOrDefaultAsync(cancellationToken);

        if (entity is null)
        {
            return Enumerable.Empty<string>();
        }

        var routes = entity.Roles.SelectMany(n => n.ViewRoutes).ToList();

        if (entity.Department is not null)
        {
            var deptRoleRoutes = entity.Department.Roles.SelectMany(n => n.ViewRoutes);

            routes.AddRange(deptRoleRoutes);
        }

        return routes;
    }

    public async Task<OperationalResult> UpdateAsync(int id, User.UpdateModel user, CancellationToken cancellationToken)
    {
        var result = new OperationalResult();

        try
        {
            var entity = await _context.Set<User>()
                .Include(x => x.Department)
                .Include(x => x.Roles)
                .Where(m => m.Id.Equals(id))
                .FirstOrDefaultAsync(cancellationToken);

            if (entity is null)
            {
                result.Status = OperationalResult.FAILURE;

                result.Message = "0 rows affected.";

                return result;
            }

            entity.Name = user.Name;
            entity.Mobile = user.Mobile;
            entity.Gender = user.Gender;
            entity.Email = user.Email;
            entity.IsLeader = user.IsLeader;
            entity.Avatar = user.Avatar;
            entity.Address = user.Address;

            if (user.DepartmentId is not null)
            {
                var entityDepartment = await _context.Set<Department>().FindAsync(new object[] { user.DepartmentId }, cancellationToken);

                if (entityDepartment is not null)
                {
                    entity.Department = entityDepartment;
                }
                else
                {
                    result.Status = OperationalResult.ERROR;
                    result.Message = $"不存在Id为{user.DepartmentId}的部门";

                    return result;
                }
            }

            if (user.DirectLeaderId is not null)
            {
                var entityDirectLeader = await _context.Set<User>().FindAsync(new object[] { user.DirectLeaderId }, cancellationToken);

                if (entityDirectLeader is not null)
                {
                    entity.DirectLeader = entityDirectLeader;
                }
                else
                {
                    result.Status = OperationalResult.ERROR;
                    result.Message = $"不存在Id为{user.DirectLeaderId}的人员";

                    return result;
                }
            }

            entity.Roles.Clear();

            foreach (var roleId in user.RolesId)
            {
                var role = await _context.Set<Role>().FindAsync(new object[] { roleId }, cancellationToken);

                if (role is not null)
                {
                    entity.Roles.Add(role);
                }
            }

            _context.Set<User>().Update(entity);

            var affectedRows = await _context.SaveChangesAsync(cancellationToken);

            result.Status = affectedRows > 0 ? OperationalResult.SUCCESS : OperationalResult.FAILURE;

            result.Message = $"{affectedRows} rows affected.";
        }
        catch (Exception error)
        {
            var exists = await _context.Set<User>().AnyAsync(m => m.Id.Equals(id), cancellationToken);

            if (!exists)
            {
                result.Status = OperationalResult.FAILURE;

                result.Message = "0 rows affected.";
            }
            else
            {
                result.Status = OperationalResult.ERROR;

                result.Message = error.ToString();
            }
        }

        return result;
    }

    private static Expression<Func<User, bool>> GetConditionSelectorCondition(User.ConditionSelectorCondition condition)
    {
        Expression<Func<User, bool>>? expression = null;

        if (condition.Keywords is not null)
        {
            Expression<Func<User, bool>>? keywords = null;
            keywords = user => user.Account.Contains(condition.Keywords);
            keywords = keywords.Or(user => user.Name.Contains(condition.Keywords));
            expression = expression?.And(keywords) ?? keywords;
        }

        if (condition.DeptId is not null)
        {
            Expression<Func<User, bool>>? dept = null;
            dept = user => condition.DeptId == user.Department.Id;
            expression = expression?.And(dept) ?? dept;
        }

        if (condition.Gender is not null)
        {
            Expression<Func<User, bool>>? gender = null;
            gender = user => condition.Gender == user.Gender;
            expression = expression?.And(gender) ?? gender;
        }

        return expression ?? (user => true);
    }

    internal static User.QueryModel GetQueryModel(User user)
    {
        var roles = user.Roles.Select(x => RoleService.GetQueryModel(x));
        var directLeader = user.DirectLeader is not null ? GetQueryModel(user.DirectLeader) : null;

        Department.QueryModel? department = null;

        if (user.Department is not null)
        {
            department = user.Department.Leader is not null && user.Department.Leader.Id == user.Id
                ? DepartmentService.GetLimitedQueryModel(user.Department)
                : DepartmentService.GetQueryModel(user.Department);
        }

        return new User.QueryModel
        {
            Id = user.Id,
            CreatedDate = user.CreatedDate,
            LastModifiedDate = user.LastModifiedDate,
            Account = user.Account,
            JobNumber = user.JobNumber,
            UserId = user.UserId,
            Name = user.Name,
            Mobile = user.Mobile,
            DirectLeader = directLeader,
            Department = department,
            Gender = user.Gender,
            Email = user.Email,
            IsLeader = user.IsLeader,
            Avatar = user.Avatar,
            QrCode = user.QrCode,
            Address = user.Address,
            Roles = roles,
        };
    }

    public async Task LoginAsync(string account, CancellationToken cancellationToken)
    {
        var entity = await _context.Set<User>()
            .Where(x => x.Account == account && !x.IsDeleted)
            .FirstOrDefaultAsync(cancellationToken);

        if (entity is null)
        {
            return;
        }

        entity.LastVisitedDate = DateTime.UtcNow;

        _context.Set<User>().Update(entity);

        await _context.SaveChangesAsync(cancellationToken);
    }
}